﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using HYCodeBase.Math;
using HYRevitCode.TransformUtility;

namespace HYRevitCode.ElmentSearchUtility.LinkDocumentSuite
{
	public static class LinkDocumentUtility
	{
		public static List<KeyValuePair<X, RevitLinkInstance>> GetAllElement<X>(Document inputDocument, BuiltInCategory inputCategory, RevitLinkInstance useLinkInstance = null) where X : Element
		{
			List<KeyValuePair<X, RevitLinkInstance>> list = new List<KeyValuePair<X, RevitLinkInstance>>();
			foreach (Element element in new FilteredElementCollector(inputDocument).OfCategory(inputCategory).WhereElementIsNotElementType())
			{
				X x = (X)((object)element);
				if (x != null)
				{
					list.Add(new KeyValuePair<X, RevitLinkInstance>(x, useLinkInstance));
				}
			}
			if (useLinkInstance == null)
			{
				foreach (Element element2 in new FilteredElementCollector(inputDocument).OfClass(typeof(RevitLinkInstance)))
				{
					RevitLinkInstance revitLinkInstance = (RevitLinkInstance)element2;
					if (revitLinkInstance != null && revitLinkInstance.GetLinkDocument() != null)
					{
						list.AddRange(LinkDocumentUtility.GetAllElement<X>(revitLinkInstance.GetLinkDocument(), inputCategory, revitLinkInstance));
					}
				}
			}
			return list;
		}

		internal static List<BaseLinkDocumentResultBean> SearchElementUseDelegate(Document inputDoc, object inputLimit, GetElementDelegate useDelgegate, bool bIfOnlyOneLink = false)
		{
			return LinkDocumentUtility.SearchElementUseDelegateCore(inputDoc, null, new List<RevitLinkInstance>(), inputLimit, useDelgegate, bIfOnlyOneLink);
		}

		internal static List<BaseLinkDocumentResultBean> SearchElementUseDelegateCore(Document inputDoc, RevitLinkInstance useLinkInstance, List<RevitLinkInstance> lstParentLinkInstance, object inputLimit, GetElementDelegate useDelgegate, bool bIfOnlyOneLink = false)
		{
			FilterLimitBean filterLimitBean = null;
			List<BaseLinkDocumentResultBean> list = new List<BaseLinkDocumentResultBean>();
			foreach (Element input in useDelgegate(inputDoc, inputLimit))
			{
				list.Add(new BaseLinkDocumentResultBean(input, useLinkInstance, lstParentLinkInstance));
			}
			if (inputLimit is FilterLimitBean)
			{
				filterLimitBean = (inputLimit as FilterLimitBean);
			}
			if (LinkDocumentUtility.IfRevitLinkInstanceIteration(useLinkInstance) || !bIfOnlyOneLink)
			{
				FilteredElementCollector filteredElementCollector = new FilteredElementCollector(inputDoc).OfClass(typeof(RevitLinkInstance));
				List<RevitLinkInstance> list2 = new List<RevitLinkInstance>();
				if (useLinkInstance != null)
				{
					list2.Add(useLinkInstance);
				}
				if (lstParentLinkInstance != null)
				{
					list2.AddRange(lstParentLinkInstance);
				}
				foreach (Element element in filteredElementCollector.ToElements())
				{
					RevitLinkInstance revitLinkInstance = (RevitLinkInstance)element;
					if (revitLinkInstance != null && revitLinkInstance.GetLinkDocument() != null)
					{
						if (filterLimitBean == null)
						{
							list.AddRange(LinkDocumentUtility.SearchElementUseDelegateCore(revitLinkInstance.GetLinkDocument(), revitLinkInstance, list2, inputLimit, useDelgegate, bIfOnlyOneLink));
						}
						else
						{
							Transform transform = revitLinkInstance.GetTotalTransform();
							if (transform != null)
							{
								transform = transform.Inverse;
							}
							list.AddRange(LinkDocumentUtility.SearchElementUseDelegateCore(revitLinkInstance.GetLinkDocument(), revitLinkInstance, list2, filterLimitBean.AddTransform(transform), useDelgegate, bIfOnlyOneLink));
						}
					}
				}
			}
			return list;
		}

		private static bool IfRevitLinkInstanceIteration(RevitLinkInstance input)
		{
			if (input == null)
			{
				return true;
			}
			RevitLinkType revitLinkType = input.Document.GetElement(input.GetTypeId()) as RevitLinkType;
			if (revitLinkType == null)
			{
				return false;
			}
			Parameter parameter = revitLinkType.get_Parameter(BuiltInParameter.RVT_LINK_REFERENCE_TYPE);
			return parameter != null && 2 == parameter.AsInteger();
		}

		public static Level FindFitLevel(ElementGeometryPacker inpuvalue, List<Level> useLevels, bool ifUseCenter)
		{
			Level level = null;
			new List<Level>();
			for (int i = 0; i < useLevels.Count - 1; i++)
			{
				if (MathUtility.Lessthan_Or_Equal(useLevels[i].Elevation, inpuvalue.MinZ) && MathUtility.Lessthan_Or_Equal(inpuvalue.MaxZ, useLevels[i + 1].Elevation))
				{
					level = useLevels[i];
					break;
				}
			}
			if (level == null)
			{
				double val = double.MaxValue;
				foreach (Level level2 in useLevels)
				{
					if (!MathUtility.LessThan(inpuvalue.MaxZ, level2.Elevation) && !MathUtility.LessThan(level2.Elevation, inpuvalue.MinZ))
					{
						double num;
						if (ifUseCenter)
						{
							num = Math.Abs(inpuvalue.CenterPoint.Z - level2.Elevation);
						}
						else
						{
							num = Math.Abs(inpuvalue.MinZPoint.Z - level2.Elevation);
						}
						if (MathUtility.LessThan(num, val))
						{
							val = num;
							level = level2;
						}
					}
				}
			}
			return level;
		}

		public static ElementGeometryPacker TryGetGeometryPacker(out bool bIfSucess, FamilyInstance inputInstance, RevitLinkInstance useLinkInstance)
		{
			Transform useTransform = null;
			if (useLinkInstance != null)
			{
				useTransform = useLinkInstance.GetTotalTransform();
			}
			return LinkDocumentUtility.TryGetGeometryPacker(out bIfSucess, inputInstance, useTransform);
		}

		public static ElementGeometryPacker TryGetGeometryPacker(out bool bIfSucess, FamilyInstance inputInstance, Transform useTransform = null)
		{
			if (LinkDocumentUtility.m_lstCategoryIdUseQuickElementGeoGeter.Contains(inputInstance.Category.Id.IntegerValue))
			{
				return LinkDocumentUtility.TryQuickGetQuickGetElementGeometryPacker(out bIfSucess, inputInstance, useTransform);
			}
			bIfSucess = true;
			GeometryElement geometryElement = inputInstance.get_Geometry(new Options());
			Transform totalTransform = inputInstance.GetTotalTransform();
			return LinkDocumentUtility.GetGeometryLimitValue(geometryElement.GetTransformed(totalTransform), useTransform);
		}

		public static ElementGeometryPacker GetGeometryLimitValue(GeometryElement inputValue, Transform useTransform = null)
		{
			List<XYZ> geometryPoints = LinkDocumentUtility.GetGeometryPoints(inputValue, useTransform);
			return LinkDocumentUtility.GetGeometryLimitValue(ref geometryPoints);
		}

		public static ElementGeometryPacker GetGeometryLimitValue(Solid inputSolid)
		{
			if (null == inputSolid)
			{
				throw new System.ArgumentNullException();
			}
			List<XYZ> list = new List<XYZ>();
			foreach (object obj in inputSolid.Faces)
			{
				Face face = (Face)obj;
				list.AddRange(face.Triangulate(0.0).Vertices);
			}
			return LinkDocumentUtility.GetGeometryLimitValue(ref list);
		}

		public static ElementGeometryPacker GetGeometryLimitValue(ref List<XYZ> allPoints)
		{
			List<XYZ> list = new List<XYZ>();
			int num;
			if (allPoints == null)
			{
				num = 0;
			}
			else
			{
				num = allPoints.Count;
			}
			if (num == 0)
			{
				return new ElementGeometryPacker(XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, false);
			}
			allPoints = (from k in allPoints
			orderby -k.Z
			select k).ToList<XYZ>();
			list.Add(allPoints[0]);
			list.Add(allPoints[num - 1]);
			allPoints = (from k in allPoints
			orderby -k.Y
			select k).ToList<XYZ>();
			list.Add(allPoints[0]);
			list.Add(allPoints[num - 1]);
			allPoints = (from k in allPoints
			orderby -k.X
			select k).ToList<XYZ>();
			list.Add(allPoints[0]);
			list.Add(allPoints[num - 1]);
			XYZ xyz = XYZ.Zero;
			int num2 = 0;
			XYZ inputCenterPoint = XYZ.Zero;
			foreach (XYZ xyz2 in allPoints)
			{
				xyz = xyz.Add(xyz2);
				num2++;
			}
			if (num2 != 0)
			{
				inputCenterPoint = xyz.Divide((double)num2);
			}
			return new ElementGeometryPacker(list[0], list[1], list[2], list[3], list[4], list[5], inputCenterPoint, true);
		}

		public static ElementGeometryPacker TryQuickGetQuickGetElementGeometryPacker(out bool bifSucess, Element input, RevitLinkInstance useLinkInstance = null)
		{
			Transform useTransForm = null;
			if (useLinkInstance != null)
			{
				useTransForm = useLinkInstance.GetTotalTransform();
			}
			return LinkDocumentUtility.TryQuickGetQuickGetElementGeometryPacker(out bifSucess, input, useTransForm);
		}

		public static ElementGeometryPacker TryQuickGetQuickGetElementGeometryPacker(out bool bifSucess, Element input, Transform useTransForm = null)
		{
			bifSucess = false;
			BoundingBoxXYZ boundingBoxXYZ = input.get_BoundingBox(null);
			if (boundingBoxXYZ == null)
			{
				return new ElementGeometryPacker(XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, false);
			}
			XYZ xyz = null;
			XYZ xyz2 = boundingBoxXYZ.Max;
			XYZ xyz3 = boundingBoxXYZ.Min;
			if (input.Location != null && input.Location is LocationPoint)
			{
				xyz = (input.Location as LocationPoint).Point;
			}
			else if (input.Location != null && input.Location is LocationCurve)
			{
				xyz = (input.Location as LocationCurve).Curve.Evaluate(0.5, true);
			}
			if (xyz == null)
			{
				return new ElementGeometryPacker(XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, XYZ.Zero, false);
			}
			if (useTransForm != null)
			{
				xyz = TransformUtilityMethod.TransformPoint(xyz, useTransForm);
				xyz2 = TransformUtilityMethod.TransformPoint(xyz2, useTransForm);
				xyz3 = TransformUtilityMethod.TransformPoint(xyz3, useTransForm);
			}
			bifSucess = true;
			return new ElementGeometryPacker(xyz2, xyz3, xyz2, xyz3, xyz2, xyz3, xyz, true);
		}

		public static List<ResultBeanWithGeometryPacker> QuickGetDocumetDoorBean(Document inpuDocument)
		{
			List<ResultBeanWithGeometryPacker> list = new List<ResultBeanWithGeometryPacker>();
            List<KeyValuePair<FamilyInstance, RevitLinkInstance>> allElement = LinkDocumentUtility.GetAllElement<FamilyInstance>(inpuDocument, BuiltInCategory.OST_Doors, null);
            foreach (KeyValuePair<FamilyInstance, RevitLinkInstance> item in LinkDocumentUtility.GetAllElement<FamilyInstance>(inpuDocument, BuiltInCategory.OST_SpecialityEquipment, null))
			{
				if (item.Key.Host != null)
				{
					allElement.Add(item);
				}
			}
			bool flag = false;
			foreach (KeyValuePair<FamilyInstance, RevitLinkInstance> keyValuePair in allElement)
			{
				ElementGeometryPacker useGeo = LinkDocumentUtility.TryQuickGetQuickGetElementGeometryPacker(out flag, keyValuePair.Key, keyValuePair.Value);
				if (flag)
				{
					list.Add(new ResultBeanWithGeometryPacker(keyValuePair.Key, useGeo, keyValuePair.Value, new List<RevitLinkInstance>()));
				}
			}
			return list;
		}

		private static List<XYZ> GetGeometryPoints(GeometryElement inputValue, Transform useTransform = null)
		{
			GeometryElement geometryElement = inputValue;
			if (useTransform != null)
			{
				geometryElement = geometryElement.GetTransformed(useTransform);
			}
			List<XYZ> list = new List<XYZ>();
			foreach (GeometryObject geometryObject in geometryElement)
			{
				if (geometryObject is Solid)
				{
					Solid solid = geometryObject as Solid;
					if (null == solid)
					{
						continue;
					}
                    IEnumerator enumerator2 = solid.Faces.GetEnumerator();
					{
						while (enumerator2.MoveNext())
						{
							object obj = enumerator2.Current;
							Face face = (Face)obj;
							list.AddRange(face.Triangulate(0.0).Vertices);
						}
						continue;
					}
				}
				if (geometryObject is GeometryInstance)
				{
					GeometryInstance geometryInstance = geometryObject as GeometryInstance;
					if (!(null == geometryInstance))
					{
						list.AddRange(LinkDocumentUtility.GetGeometryPoints(geometryInstance.GetInstanceGeometry(geometryInstance.Transform), null));
					}
				}
			}
			return list;
		}

		public static double CalculateTransformEffectOnZ(Transform input)
		{
			double result = 0.0;
			if (input != null)
			{
				XYZ zero = XYZ.Zero;
				result = TransformUtilityMethod.TransformPoint(zero, input).Z - zero.Z;
			}
			return result;
		}

		public static bool TryGetLevelNearElvation(Document useDoc, double inputElvation, out Level findedLevel, double tolerance = 0.0)
		{
			if (useDoc == null)
			{
				throw new System.ArgumentNullException();
			}
			bool result = false;
			findedLevel = null;
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(useDoc);
			filteredElementCollector.OfClass(typeof(Level));
			foreach (Element element in filteredElementCollector.ToElements())
			{
				if (element != null && element is Level && MathUtility.IsEqual((element as Level).Elevation, inputElvation, tolerance))
				{
					result = true;
					findedLevel = (element as Level);
					break;
				}
			}
			return result;
		}

		public static bool TryGetViewPlanNearElvation(Document useDoc, double inputElvation, out List<ViewPlan> findedViewPlan, double tolerance = 0.0)
		{
			findedViewPlan = null;
			Level level;
			bool flag = LinkDocumentUtility.TryGetLevelNearElvation(useDoc, inputElvation, out level, tolerance);
			if (!flag)
			{
				return flag;
			}
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(useDoc);
			filteredElementCollector.OfClass(typeof(ViewPlan)).WhereElementIsNotElementType();
			flag = false;
			foreach (Element element in filteredElementCollector.ToElements())
			{
				if (element != null && element is ViewPlan && (element as ViewPlan).GenLevel != null && (element as ViewPlan).GenLevel.Id.IntegerValue == level.Id.IntegerValue)
				{
					if (findedViewPlan == null)
					{
						findedViewPlan = new List<ViewPlan>();
					}
					findedViewPlan.Add(element as ViewPlan);
					flag = true;
				}
			}
			return flag;
		}

		public static void GetViewBox(ViewPlan viewPlan, List<double> m_lstLevelValue, ref BoundingBoxIntersectsFilter filterIntersect)
		{
			double elevation = viewPlan.GenLevel.Elevation;
			double nearTopElevation = LinkDocumentUtility.GetNearTopElevation(elevation, m_lstLevelValue);
			BoundingBoxUV outline = viewPlan.Outline;
			UV uv = outline.Min * (double)viewPlan.Scale;
			UV uv2 = outline.Max * (double)viewPlan.Scale;
			Outline outline2 = new Outline(new XYZ(uv.U, uv.V, elevation), new XYZ(uv2.U, uv2.V, nearTopElevation));
			filterIntersect = new BoundingBoxIntersectsFilter(outline2);
		}

		public static double GetNearTopElevation(double dCurElevation, List<double> m_lstLevelValue)
		{
			for (int i = 0; i < m_lstLevelValue.Count - 1; i++)
			{
				double val = m_lstLevelValue[i];
				double num = m_lstLevelValue[i + 1];
				if (MathUtility.LessThan(dCurElevation, num) && MathUtility.GreaterThan(dCurElevation, val))
				{
					return num;
				}
			}
			return dCurElevation + 3.0;
		}

		private static List<int> m_lstCategoryIdUseQuickElementGeoGeter = new List<int>
		{
			-2000023
		};
	}
}
